package demo.dubbo;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.config.AbstractConfig;
import com.alibaba.dubbo.config.DubboShutdownHook;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.ServiceConfig;
import com.alibaba.dubbo.config.spring.ServiceBean;
import com.alibaba.dubbo.config.spring.schema.DubboBeanDefinitionParser;
import com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler;
import com.alibaba.dubbo.remoting.exchange.Exchanger;
import com.alibaba.dubbo.rpc.Exporter;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Protocol;
import com.alibaba.dubbo.rpc.ProxyFactory;
import com.alibaba.dubbo.rpc.Result;

public class DubboDemo {
    /**
     * <h1>解析服务</h1> 基于 dubbo.jar 内的 META-INF/spring.handlers 配置，Spring 在遇到 dubbo 名称空间时，会回调 DubboNamespaceHandler。
     * 所有dubbo 的标签，都统一用 DubboBeanDefinitionParser 进行解析，基于一对一属性映射，将 XML 标签解析为 Bean 对象。
     * 
     * 在 ServiceConfig.export() 或 ReferenceConfig.get() 初始化时，将 Bean 对象转换 URL 格式，所有 Bean 属性转成 URL 的参数。
     * 
     * 然后将 URL 传给 协议扩展点，基于扩展点的 扩展点自适应机制，根据 URL 的协议头，进行不同协议的服务暴露或引用。
     */
    public void init(DubboNamespaceHandler dubboNamespaceHandler) {
        // Dubbo命名空间处理器初始化
        dubboNamespaceHandler.init();
        // 服务发布
        DubboBeanDefinitionParser serviceBeanParser = new DubboBeanDefinitionParser(ServiceBean.class, true);

    }

    /**
     * 而在Spring初始化完成Bean的组装，会调用InitializingBean的afterPropertiesSet方法，在Spring容器加载完成，会接收到事件ContextRefreshedEvent，
     * 调用ApplicationListener的onApplicationEvent方法。
     * 在afterPropertiesSet中，和onApplicationEvent中，会调用export()，在export()中，会暴露dubbo服务，具体区别在于是否配置了delay属性(比如delay=”5000”)，是否延迟暴露。
     * 如果delay不为null，或者不为-1时，会在afterPropertiesSet中调用export()暴露dubbo服务，
     * 如果为null,或者为-1时，会在Spring容器初始化完成，接收到ContextRefreshedEvent事件，调用onApplicationEvent，暴露dubbo服务。
     * 
     * <li>ServiceBean实现了Spring提供的接口InitializingBean，当在完成Bean初始化和注入的时候程序会自动执行InitializingBean接口中的afterPropertiesSet方法。
     * <li>在这个方法中，会对dubbo的配置做初始化赋值，即我们在配置文件中配置的：provider，application，module，registries，monitor，protocols，path（beanName）
     * <li>在方法的最后会调用父类ServiceConfig中的export方法。
     * <li>export方法判断是否延迟注册，延迟注册使用新的守护线程去执行暴露服务，否则直接暴露服务。
     * <li>在doExport方法中，对要暴露的服务进行了一系列的检查，检查provider，application，module，registries，monitor这些参数是否为空，
     * 是否是GenericService类型的服务，检查要注册的bean的引用和方法等。在方法的最后会调用doExportUrls方法。
     */
    public void serviceBean(ServiceBean serviceBean) throws Exception {
        serviceBean.afterPropertiesSet();
    }

    public void invoker(Invoker invoker, Invocation invocation) {
        URL url = invoker.getUrl();
        Class<?> cls = invoker.getInterface();
        Result result = invoker.invoke(invocation);
    }

    public void exporter(Exporter exporter) {
        Invoker invoker = exporter.getInvoker();
        invoker.destroy();
        exporter.unexport();

        Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
        Exchanger exchanger = null;

        ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
        ServiceConfig serviceConfig = null;
        // 服务暴露过程
        serviceConfig.export();
    }

    /**
     * 说说这些类及方法的概念作用：
     * <li>1、proxyFactory:就是为了获取一个接口的代理类，例如获取一个远程接口的代理。 它有2个方法，代表2个作用
     * a、getInvoker:针对server端，将服务对象，例如DemoServiceImpl包装成一个Wrapper对象。 b、getProxy：针对client端，创建接口的代理对象，例如DemoService的接口。
     * 
     * <li>2、makeWrapper：它类似spring的BeanWrapper，它就是包装了一个接口或一个类，可以通过Wrapper对实例对象进行赋值取值以及指定方法的调用。
     * 
     * <li>3、Invoker：它是一个可执行的对象，能够根据方法名称、参数得到相应的执行结果。 它里面有一个很重要的方法Result invoke(Invocation
     * invocation)，Invocation是包含了需要执行的方法和参数的重要信息，目前它只有2个实现类RpcInvocation MockInvocation 它有3种类型的Invoker 1、本地执行类的Invoker
     * 2、远程通信类的Invoker 3、多个远程通信执行类的Invoker聚合成集群版的Invoker
     * <li>4、Protocol： 1）export暴露远程服务（用语服务端），就是将proxyFactory.getInvoker创建的代理类invoker对象，通过协议暴露给外部。 2）refer：引用远程服务（用于客户端）
     * 
     * <li>5、Exporter：维护invoker的生命周期
     * 
     * <li>6、exchanger：信息交换层，封装请求响应模式同步转异步
     * 
     * <li>7、transporter：网络传输层，用来抽象Netty（dubbo默认）或者Mina的统一接口
     */
    public void tes() {

    }

    /**
     * <h1>优雅停机</h1>是指在停止应用时，执行的一系列保证应用正常关闭的操作。 这些操作往往包括等待已有请求执行完成、关闭线程、关闭连接和释放资源等，
     * 优雅停机可以避免非正常关闭程序可能造成数据异常或丢失，应用异常等问题。优雅停机本质上是JVM即将关闭前执行的一些额外的处理代码。 适用场景:
     * <li>JVM主动关闭(System.exit(int)；
     * <li>JVM由于资源问题退出(OOM)；
     * <li>应用程序接受到SIGTERM或SIGINT信号。
     * <p>
     * <h1>配置方式:</h1>
     * <li>服务的优雅停机:在Dubbo中，优雅停机是默认开启的，停机等待时间为10000毫秒。可以通过配置dubbo.service.shutdown.wait来修改等待时间。
     * <li>容器的优雅停机:当使用org.apache.dubbo.container.Main这种容器方式来使用 Dubbo 时，也可以通过配置dubbo.shutdown.hook为true来开启优雅停机。
     * <li>通过QOS优雅上下线:基于ShutdownHook方式的优雅停机无法确保所有关闭流程一定执行完，所以 Dubbo 推出了多段关闭的方式来保证服务完全无损。
     * 多段关闭即将停止应用分为多个步骤，通过运维自动化脚本或手工操作的方式来保证脚本每一阶段都能执行完毕。 在关闭应用前，首先通过 QOS的offline指令下线所有服务，
     * 然后等待一定时间确保已经到达请求全部处理完毕，由于服务已经在注册中心下线，当前应用不会有新的请求。这时再执行真正的关闭(SIGTERM 或SIGINT)流程，就能保证服务无损。
     * 
     * <h1>实现原理</h1>
     * <li>在加载类org.apache.dubbo.config.AbstractConfig时，通过org.apache.dubbo.config.DubboShutdownHook 向JVM注册 ShutdownHook
     * <li>每个ShutdownHook都是一个单独的线程，由JVM在退出时触发执行org.apache.dubbo.config.DubboShutdownHook。
     * <li>首先关闭所有注册中心，这一步包括： 1、从注册中心注销所有已经发布的服务； 2、取消订阅当前应用所有依赖的服务； 3、断开与注册中心的连接。
     * <li>执行所有Protocol的destroy()，主要包括： 1、销毁所有Invoker和Exporter；
     * 2、关闭Server，向所有已连接Client发送当前Server只读事件；3、关闭独享/共享Client，断开连接，取消超时和重试任务； 4、释放所有相关资源。
     * <li>执行完毕，关闭JVM。
     * 
     * <h1>注意事项</h1>
     * <li>使用SIGKILL关闭应用不会执行优雅停机；
     * <li>优雅停机不保证会等待所有已发送/到达请求结束；
     * <li>配置的优雅停机等待时间timeout不是所有步骤等待时间的总和，而是每一个destroy执行的最大时间。例如配置等待时间为5秒，则关闭Server、关闭Client等步骤会分别等待5秒。
     */
    public void destory() {
        AbstractConfig config = null;
        /** 单例模式，因为 {@link ApplicationShutdownHooks} use IdentityHashMap to store the shutdown hooks. */
        DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook();
    }

    /**
     * Provider在接收到停机指令后
     * <li>从注册中心上注销所有服务；
     * <li>从配置中心取消监听动态配置；
     * <li>向所有连接的客户端发送只读事件，停止接收新请求；
     * <li>等待一段时间以处理已到达的请求，然后关闭请求处理线程池；
     * <li>断开所有客户端连接。
     * <p>
     * 当使用容器方式运行 Dubbo 时，在容器准备退出前，可进行一系列的资源释放和清理工。 例如使用 SpringContainer时，Dubbo
     * 的ShutdownHook线程会执行ApplicationContext的stop和close方法，保证 Bean的生命周期完整。
     */
    public void destroyProvider() {
    }

    /**
     * Consumer在接收到停机指令后
     * <li>拒绝新到请求，直接返回调用异常；
     * <li>等待当前已发送请求执行完毕，如果响应超时则强制关闭连接。
     */
    public void destroyConsumer() {
    }
}
